using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace DebugConsole.Components
{
    /// <summary>
    /// Displays a hierarchy of values that need to be traced.
    /// </summary>
    public class TreeView : DebugComponent
    {
        private TreeViewNode root;
        private int iterationCount;
        SpriteBatch spriteBatch;
        SpriteFont font;

        Texture2D closedTexture;
        Texture2D openedTexture;

        Vector2 Position;
        MouseState prevMouse;

        #region Constructor
        public TreeView(DebugSharedResources resources) : base(resources)
        {
            root = new TreeViewNode("root");

            this.font = resources.content.Load<SpriteFont>("Default");
            this.openedTexture = resources.content.Load<Texture2D>("close");
            this.closedTexture = resources.content.Load<Texture2D>("open");
            this.spriteBatch = resources.spriteBatch;
            this.Position = new Vector2(30, 15);

            HandleCommand("show");
            HandleCommand("trace");
        }
        #endregion

        #region Get
        /// <summary>
        /// Retrieves a value that is stored in the TreeView.
        /// </summary>
        public Object Get(String key)
        {
            //if (!table.ContainsKey(key))
            //    Set(key, 0);
            //return ((DebugItem)table[key]).value;
            return (Int32)0;
        }
        #endregion


        #region Execute
        public override void Execute(string[] command, params object[] arguments)
        {
            StringBuilder value = new StringBuilder();
            // We need at least the command, the key and a value. Either string or object.
            if (command.Length < 3)
            {
                if (command.Length == 2 && arguments.Length >= 1)
                {
                }
                else
                {
                    return;
                }

            }
            
            for (int i = 2 ; i < command.Length; i++)
            {
                value.Append(command[i]);
            }
            for (int i = 0 ; i < arguments.Length; i++)
            {
                value.Append(arguments[i].ToString());
            }
            Set(command[1], value);
        }
        #endregion


        #region Set
        /// <summary>
        /// Sets the value of a given key, if the key is not present
        /// in the tree, then is added.
        /// </summary>
        /// <param name="key"></param>
        public void Set(String key, Object value)
        {
            bool foundKey = false;
            TreeViewNode currentNode = root;
            String remainingName = key;
            while (!foundKey)
            {
                iterationCount++;
                String subName;
                bool foundSubKey = false;

                int dotIndex = remainingName.IndexOf('.');
                if (dotIndex < 0 || dotIndex >= remainingName.Length)
                {
                    subName = remainingName;
                    remainingName = "";
                    foundKey = true;
                }
                else
                {
                    subName = remainingName.Remove(dotIndex,remainingName.Length - dotIndex);
                    remainingName = remainingName.Substring(subName.Length + 1);
                }

                foreach (TreeViewNode node in currentNode.nodes)
                {
                    if (node.name == subName)
                    {
                        currentNode = node;
                        foundSubKey = true;     // A part of the key was found.
                        break;
                    }
                }
                if (!foundSubKey)
                {
                    // If there's no subKey, we create it.
                    TreeViewNode newNode = new TreeViewNode(subName);
                    currentNode.nodes.Add(newNode);
                    currentNode = newNode;
                    foundSubKey = true;     // A part of the key was found (created).
                }
            }
            currentNode.value = value;
        }
        #endregion

        #region Update
        public override void Update(GameTime gameTime)
        {
#if XBOX360
            return;
#endif
            MouseState mouse = Mouse.GetState();
            if (mouse.LeftButton == ButtonState.Released && prevMouse.LeftButton == ButtonState.Pressed)
            {
                LeftClick(new Vector2(mouse.X, mouse.Y));
            }
            prevMouse = mouse;
        }
        #endregion

        #region LeftClick
        public void LeftClick(Vector2 clickPos)
        {
            int position = 12;
            foreach (TreeViewNode node in root.nodes)
            {
                position = CheckLeftRecursively(node, position, 1, clickPos);
            }
        }

        private int CheckLeftRecursively(TreeViewNode node, int position, int level, Vector2 clickPos)
        {
            int newPosition = position + 12;

            Rectangle rect = new Rectangle(level * 11, position, closedTexture.Width, closedTexture.Height);
            if (rect.Contains((int)clickPos.X, (int)clickPos.Y))
            {
                node.Toggle();
                return newPosition;
            }
            if (node.open)
            {
                foreach (TreeViewNode n in node.nodes)
                {
                    newPosition = CheckLeftRecursively(n, newPosition, level + 1, clickPos);
                }
            }
            return newPosition;
        }

        #endregion

        #region Draw
        public override void Draw(GameTime gameTime)
        {
            this.spriteBatch.Begin();
            int position = 12;
            foreach (TreeViewNode node in root.nodes)
            {
                position = DrawRecursively(node, position, 1);
            }
            this.spriteBatch.End();
        }

        private int DrawRecursively(TreeViewNode node, int position, int level)
        {
            int newPosition = position + 12;
            DrawNode(node, position, level);
            if (node.open)
            {
                foreach (TreeViewNode n in node.nodes)
                {
                    newPosition = DrawRecursively(n, newPosition, level + 1);
                }
            }
            return newPosition;
        }

        
        private void DrawText(String text, Vector2 drawPosition)
        {
            spriteBatch.DrawString(font, text, drawPosition + new Vector2(-1, 0), Color.Black);
            spriteBatch.DrawString(font, text, drawPosition + new Vector2(0, 1), Color.Black);
            spriteBatch.DrawString(font, text, drawPosition + new Vector2(0, -1), Color.Black);
            spriteBatch.DrawString(font, text, drawPosition + new Vector2(1, 0), Color.Black);
            spriteBatch.DrawString(font, text, drawPosition, Color.White);
        }

        private void DrawNode(TreeViewNode node, int position, int level)
        {
            Vector2 drawPosition = new Vector2(this.Position.X + level * 11, this.Position.Y + position);
            if (node.nodes.Count == 0)
            {
                DrawText(node.name + ": " + TextHelper.FormatForDebug(node.value), drawPosition);
            }
            else
            {
                if (!node.open)
                    spriteBatch.Draw(closedTexture, drawPosition, Color.White);
                else
                    spriteBatch.Draw(openedTexture, drawPosition, Color.White);
                DrawText(node.name, drawPosition + Vector2.UnitX * 12);
            }
            
            
        }
        #endregion

    }
}
